home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Mac Game Programming Gurus
/
TricksOfTheMacGameProgrammingGurus.iso
/
Book Chapters
/
06 - Audio
/
Example Code
/
Hollywood.c
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
UTF-8
Wrap
Text File
|
1995-06-16
|
43.9 KB
|
1,831 lines
|
[
TEXT/MMCC
]
//----------------
// Hollywood API for use with the Macintosh Sound Manager 3.0
//
// This code depends upon Universal Interfaces 2.0a3 or better from Apple Computer, Inc
//
// History
// 1/8/95 Created by Steve Hales
//----------------
#include <Types.h>
#include <Memory.h>
#include <Quickdraw.h>
#include <OSEvents.h>
#include <Desk.h>
#include <Events.h>
#include <Resources.h>
#include <Windows.h>
#include <Fonts.h>
#include <TextEdit.h>
#include <Menus.h>
#include <Dialogs.h>
#include <ToolUtils.h>
#include <Retrace.h>
#include <StandardFile.h>
#include <Sound.h>
#include <SoundInput.h>
#include <AIFF.h>
#include <Gestalt.h>
#include <Errors.h>
#include <Traps.h>
#include <ConditionalMacros.h>
#include <FixMath.h>
#ifndef __MIXEDMODE__
#include <SysEqu.h>
#else
#include <LowMem.h>
#define LMGetSoundActive() (* (unsigned char *) 0x27E)
#endif
#if THINK_C
#include <Think.h>
#else
#define TRUE true
#define FALSE false
#endif
#include "Hollywood.h"
#if 0
#define DEBUGSTR(x)
#else
#define DEBUGSTR(x) DebugStr(x)
#endif
#define kMaxCallbackQueue 50 // total number of queued events
// Structures
struct MM_SoundVoice
{
SndChannelPtr theChannel;
CustomCallbackProc customCallback;
short int voiceNumber;
long userData;
UnsignedFixed lastRate;
ExtSoundHeader theSndBuffer;
Boolean voiceActive;
Boolean voicePaused;
Boolean filePlay;
FSSpec fileSpec;
short int fileRef;
};
typedef struct MM_SoundVoice MM_SoundVoice;
// This queue is put together via an interrupt to process events at non-interrupt time
struct MM_CallbackQueue
{
CustomCallbackProc customCallback;
short int voiceNumber;
long userData;
short int filePlayRef; // 0 = no file play to close
Boolean active;
};
typedef struct MM_CallbackQueue MM_CallbackQueue;
// Variables
static long globalA5;
static short int maxSoundVoices = 0; // Max voices allocated for Sound Manager 3.0
static MM_SoundVoice *theSoundVoices = NULL;
static MM_CallbackQueue theCallbackQueue[kMaxCallbackQueue];
static long * taskPtr;
static VBLTask * theSoundVBPtr;
static void (*vblankProcPtr)(void);
static long totalRegisteredSounds;
static long currentBlockSizeForRegisteredSounds;
static SndReference * registeredSounds; // current list of registered sounds
#ifdef __MIXEDMODE__
SndCallBackUPP theMasterSoundCallbackProcPtr;
#else
SndCallBackProcPtr theMasterSoundCallbackProcPtr;
#endif
// Internal Defines
#define kSoundManagerID 'sm' // used to fix a bug in the Sound Manager. It pre-fires the callback, so
// you must check that its our callback. We do this by 'wasting' the param1.
// Note that param1 is only 16 bits.
#define kMaxSoundManagerVoices 4 // total number of voices that the Sound Manager can allocate. This may change
// as CPU performance increases
#define kRegisterSoundBlockSize 40 // inital number of sounds that can be registered without reallocating the
// block array
// forward references
static pascal void HYP_HandleSoundDoneCallBack(SndChannelPtr pChannel, register SndCommand *pCmd);
static void SHYP_Callback(short int voiceNumber, short int what, long userData);
static OSErr HYP_PostCallbackFunction(SndChannelPtr pChannel, long userData);
static OSErr HYP_AddToCallbackQueue(MM_CallbackQueue *newQueue);
// Private functions
//
// This will return TRUE if the new sound manager is installed, otherwise FALSE
static Boolean HYP_IsNewSoundManagerInstalled(void)
{
NumVersion theVersion;
void * trap1;
void * trap2;
Boolean installed;
installed = FALSE;
trap1 = (void *)GetToolTrapAddress(_SoundDispatch); /* SndSoundManagerVersion Trap */
trap2 = (void *)GetToolTrapAddress(_Unimplemented); /* Unimplemented Trap */
if (trap1 != trap2)
{
theVersion = SndSoundManagerVersion();
if (theVersion.majorRev >= 3)
{
installed = TRUE;
}
}
return installed;
}
// This will return TRUE if the virtual memory manager is installed, otherwise FALSE.
static Boolean HYP_IsVirtualMemoryAvailable(void)
{
long feature;
feature = 0;
if (Gestalt(gestaltVMAttr, &feature) == noErr)
{
if (feature & (1<<gestaltVMPresent))
{
return TRUE;
}
}
return FALSE;
}
static short int HYP_GetFreeVoice(void)
{
register short int count, freeVoice;
freeVoice = kUseAnyVoice;
for (count = 0; count < maxSoundVoices; count++)
{
if (theSoundVoices[count].voiceActive == FALSE)
{
freeVoice = count;
break;
}
}
return freeVoice;
}
static MM_SoundVoice * HYP_GetPrivateDataFromVoice(register short int voice)
{
register MM_SoundVoice *pVoice;
pVoice = NULL;
if (theSoundVoices)
{
if ( (voice < maxSoundVoices) && (voice >= 0) )
{
if (theSoundVoices[voice].theChannel)
{
pVoice = &theSoundVoices[voice];
}
}
}
return pVoice;
}
static short int HYP_GetVoiceFromChannel(register SndChannelPtr pChannel)
{
register short int count;
count = -1;
for (count = 0; count < maxSoundVoices; count++)
{
if (pChannel == theSoundVoices[count].theChannel)
{
break;
}
}
return count;
}
// For use with Apple's Sound Manager
static pascal void HYP_FilePlayCompletionDone(SndChannelPtr pChannel)
{
register long saveA5;
register MM_SoundVoice *pVoice;
register short int voiceNumber;
MM_CallbackQueue newQueue;
#if GENERATING68K == 1
saveA5 = SetA5(pChannel->userInfo); /* restore previous a5 */
#else
saveA5;
#endif
voiceNumber = HYP_GetVoiceFromChannel(pChannel);
pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
if (pVoice)
{
if (pChannel != pVoice->theChannel)
{
DEBUGSTR("\pChannel doesn't match voiceNumber");
}
if (pVoice->customCallback)
{
(*pVoice->customCallback)(voiceNumber,
kSoundDoneNormalStop,
pVoice->userData);
newQueue.customCallback = pVoice->customCallback;
newQueue.voiceNumber = pVoice->voiceNumber;
newQueue.userData = pVoice->userData;
newQueue.filePlayRef = pVoice->fileRef;
if (HYP_AddToCallbackQueue(&newQueue))
{
DEBUGSTR("\pQueue Full!");
}
}
pVoice->voiceActive = FALSE;
}
/* Restore A5 for the rest of the interupt process
*/
#if GENERATING68K == 1
SetA5(saveA5); /* restore previous a5 */
#endif
}
static pascal void HYP_HandleSoundDoneCallBack(SndChannelPtr pChannel, register SndCommand *pCmd)
{
register long saveA5;
register MM_SoundVoice *pVoice;
register short int voiceNumber;
MM_CallbackQueue newQueue;
if (pCmd->param1 == kSoundManagerID)
{
#if GENERATING68K == 1
saveA5 = SetA5(pChannel->userInfo); /* restore previous a5 */
#else
saveA5;
#endif
voiceNumber = HYP_GetVoiceFromChannel(pChannel);
pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
if (pVoice)
{
if (pChannel != pVoice->theChannel)
{
DEBUGSTR("\pChannel doesn't match voiceNumber");
}
if (pVoice->customCallback)
{
(*pVoice->customCallback)(voiceNumber,
kSoundDoneNormalStop,
pVoice->userData);
newQueue.customCallback = pVoice->customCallback;
newQueue.voiceNumber = pVoice->voiceNumber;
newQueue.userData = pVoice->userData;
newQueue.filePlayRef = 0;
if (HYP_AddToCallbackQueue(&newQueue))
{
DEBUGSTR("\pQueue Full!");
}
}
pVoice->voiceActive = FALSE;
}
/* Restore A5 for the rest of the interupt process
*/
#if GENERATING68K == 1
SetA5(saveA5); /* restore previous a5 */
#endif
}
}
static OSErr HYP_PostCallbackFunction(SndChannelPtr pChannel, long userData)
{
SndCommand theCmd;
OSErr theErr;
if (pChannel)
{
theCmd.cmd = callBackCmd;
theCmd.param1 = kSoundManagerID; // used for ID. Bug in SM that a callback is
// sometimes called at the begining of a sample
// with the wrong information.
// Use this to make sure that it is our callback.
theCmd.param2 = userData;
pChannel->userInfo = globalA5;
theErr = SndDoCommand(pChannel, &theCmd, FALSE);
}
else
{
theErr = badChannel;
}
return theErr;
}
static void HYP_SetupQueues(void)
{
short int count;
for (count = 0; count < kMaxCallbackQueue; count++)
{
theCallbackQueue[count].active = FALSE;
theCallbackQueue[count].customCallback = NULL;
}
}
static void HYP_CleanupQueues(void)
{
}
static OSErr HYP_AddToCallbackQueue(MM_CallbackQueue *newQueue)
{
OSErr theErr;
short int count;
Boolean foundQueue;
// DEBUGSTR("\pHYP_AddToCallbackQueue");
theErr = noErr;
foundQueue = FALSE;
for (count = 0; count < kMaxCallbackQueue; count++)
{
if (theCallbackQueue[count].active == FALSE)
{
theCallbackQueue[count] = *newQueue;
theCallbackQueue[count].active = TRUE;
foundQueue = TRUE;
break;
}
}
if (foundQueue == FALSE)
{
theErr = qErr;
}
return theErr;
}
static void HYP_ProcessNextCallbackQueue(void)
{
register MM_CallbackQueue * theQueue;
short int count;
// DEBUGSTR("\pHYP_ProcessNextCallbackQueue");
for (count = 0; count < kMaxCallbackQueue; count++)
{
theQueue = &theCallbackQueue[count];
if (theQueue->active)
{
// DEBUGSTR("\pHYP_ProcessNextCallbackQueue:BEFORE CALLBACK");
if (theQueue->filePlayRef)
{
FSClose(theQueue->filePlayRef);
theQueue->filePlayRef = 0;
}
if (theQueue->customCallback)
{
(*theQueue->customCallback)(theQueue->voiceNumber, kSoundDoneNoInterrupt, theQueue->userData);
theQueue->customCallback = NULL;
}
theQueue->active = FALSE;
}
}
}
// General purpose VBL task to execute user defined task
static void HYP_ProcessVBLTask(void)
{
if (vblankProcPtr)
{
(*vblankProcPtr)();
}
}
#if GENERATING68K == 1
// 68k based VBL task function
#pragma parameter __D0 GetA0toVariable
pascal long GetA0toVariable(void) = {0x2028, 0xFFFC};
static pascal void HYP_PreProcessVBLTask(void)
{
long saveA5, newA5;
newA5 = GetA0toVariable(); /* Globals register points the vbl task structure */
saveA5 = SetA5(newA5); /* set current a5 */
theSoundVBPtr->vblCount = 1; /* tell it to contiue */
HYP_ProcessVBLTask();
/* Restore A5 for the rest of the interupt process
*/
SetA5(saveA5); /* restore previous a5 */
}
#else
// PowerPC based VBL task function
static pascal void HYP_PreProcessVBLTask(VBLTaskPtr theVBLTask)
{
theSoundVBPtr->vblCount = 1; /* tell it to contiue */
HYP_ProcessVBLTask();
}
#endif
#if GENERATING68K == 1
// 68k based VBL task setup and cleanup
static OSErr HYP_SetupVBLTask(void)
{
OSErr theErr;
struct Jump
{
short bsr_inderect; // 0
void * address; // 4
long movea; // 8
short jmp_a1; // 12
};
struct Jump *heapJumpPtr;
/*
0x6104, bsr.s 6(pc)
0x0000, 0x0000, dc.l 0
0x225F movea.l (a7)+, a1
0x2251 move (a1), a1
0x4Ed1 jmp (a1)
*/
/* Set up Vertical Blank Interrupt
*/
taskPtr = (long *)NewPtrClear((long)sizeof(VBLTask) + sizeof(long));
heapJumpPtr = (struct Jump *)NewPtrSys((long)sizeof(struct Jump));
if ( (taskPtr) && (heapJumpPtr) )
{
if (HYP_IsVirtualMemoryAvailable())
{
LockMemory(taskPtr, (long)sizeof(VBLTask) + sizeof(long));
LockMemory(heapJumpPtr, (long)sizeof(struct Jump));
}
theSoundVBPtr = (VBLTask *)( ((Byte *)taskPtr) + sizeof(long) );
taskPtr[0] = SetCurrentA5();
heapJumpPtr->bsr_inderect = 0x6104;
heapJumpPtr->address = HYP_PreProcessVBLTask;
heapJumpPtr->movea = 0x225F2251L;
heapJumpPtr->jmp_a1 = 0x4Ed1;
theSoundVBPtr->vblAddr = (VBLUPP)heapJumpPtr;
theSoundVBPtr->vblCount = 1; /* Every 1/60th of a second */
theSoundVBPtr->qType = vType;
theSoundVBPtr->qLink = NULL;
theSoundVBPtr->vblPhase = 0;
/* Ok, flush the code/data cache for the '040 Macs. */
#ifndef __MIXEDMODE__
if (*((char *)CPUFlag) >= 2)
#else
if (LMGetCPUFlag() >= 2)
#endif
{
FlushInstructionCache();
FlushDataCache();
}
theErr = VInstall((QElemPtr)theSoundVBPtr);
}
else
{
if (heapJumpPtr)
{
if (HYP_IsVirtualMemoryAvailable())
{
UnlockMemory((Ptr)heapJumpPtr, 12L);
}
DisposePtr((Ptr)heapJumpPtr);
}
if (taskPtr)
{
if (HYP_IsVirtualMemoryAvailable())
{
UnlockMemory(taskPtr, (long)sizeof(VBLTask) + sizeof(long));
}
DisposePtr((Ptr)taskPtr);
taskPtr = NULL;
theSoundVBPtr = NULL;
}
}
return theErr;
}
static OSErr HYP_CleanupVBLTask(void)
{
if (theSoundVBPtr)
{
if (theSoundVBPtr->vblAddr)
{
if (HYP_IsVirtualMemoryAvailable())
{
UnlockMemory((Ptr)theSoundVBPtr->vblAddr, 12L);
}
DisposePtr((Ptr)theSoundVBPtr->vblAddr);
}
VRemove((QElemPtr)theSoundVBPtr);
theSoundVBPtr = NULL;
}
if (taskPtr)
{
if (HYP_IsVirtualMemoryAvailable())
{
UnlockMemory(taskPtr, (long)sizeof(VBLTask) + sizeof(long));
}
DisposePtr((Ptr)taskPtr);
}
return noErr;
}
#else
// PowerPC based VBL task setup and cleanup
static OSErr HYP_SetupVBLTask(void)
{
OSErr theErr;
/* Set up Vertical Blank Interrupt
*/
theErr = noErr;
taskPtr = NULL;
theSoundVBPtr = (VBLTask *)NewPtrSysClear((long)sizeof(VBLTask));
if (theSoundVBPtr)
{
if (HYP_IsVirtualMemoryAvailable())
{
LockMemory(theSoundVBPtr, (long)sizeof(VBLTask));
}
theSoundVBPtr->vblAddr = NewVBLProc(HYP_PreProcessVBLTask);
theSoundVBPtr->vblCount = 1; /* Every 1/60th of a second */
theSoundVBPtr->qType = vType;
theSoundVBPtr->qLink = NULL;
theSoundVBPtr->vblPhase = 0;
theErr = VInstall((QElemPtr)theSoundVBPtr);
}
return theErr;
}
static OSErr HYP_CleanupVBLTask(void)
{
if (theSoundVBPtr)
{
if (theSoundVBPtr->vblAddr)
{
DisposeRoutineDescriptor((VBLUPP)theSoundVBPtr->vblAddr);
theSoundVBPtr->vblAddr = NULL;
}
VRemove((QElemPtr)theSoundVBPtr);
if (HYP_IsVirtualMemoryAvailable())
{
UnlockMemory(theSoundVBPtr, (long)sizeof(VBLTask));
}
DisposePtr((Ptr)theSoundVBPtr);
theSoundVBPtr = NULL;
}
return noErr;
}
#endif
static Boolean HYP_IsThisSoundRegistered(SndReference theSound)
{
register short int count;
register Boolean foundPlace;
foundPlace = FALSE;
for (count = 0; count < totalRegisteredSounds; count++)
{
if (registeredSounds[count] == theSound)
{
foundPlace = TRUE;
}
}
return foundPlace;
}
static void HYP_RegisterThisSound(SndReference theSound)
{
register short int count;
register Boolean foundPlace;
SndReference * newArray;
register long theSize;
if (registeredSounds == NULL) // first time to register?
{
theSize = (long)sizeof(SndReference *) * kRegisterSoundBlockSize;
registeredSounds = (SndReference *)NewPtrClear(theSize);
totalRegisteredSounds = 0;
currentBlockSizeForRegisteredSounds = kRegisterSoundBlockSize;
}
if (registeredSounds)
{
if (HYP_IsThisSoundRegistered(theSound) == FALSE) // don't register a sound twice
{
// walk through the sound register array and see if there are any free blocks, if so, then put
// our reference there
foundPlace = FALSE;
for (count = 0; count < totalRegisteredSounds; count++)
{
if (registeredSounds[count] == NULL)
{
registeredSounds[count] = theSound;
foundPlace = TRUE;
}
}
// if we didn't find a place in our array, go ahead and put it into the next place. if we've need more
// in the array then reallocate and then place it in there
if (foundPlace == FALSE)
{
totalRegisteredSounds++; // next entry
if (totalRegisteredSounds > currentBlockSizeForRegisteredSounds)
{
// out of space, so reallocate a new array and copy all the current registered sounds into it
theSize = sizeof(SndReference *) * (currentBlockSizeForRegisteredSounds + kRegisterSoundBlockSize);
newArray = (SndReference *)NewPtrClear(theSize);
if (newArray)
{
theSize = (long)sizeof(SndReference *) * currentBlockSizeForRegisteredSounds;
BlockMove((Ptr)registeredSounds, (Ptr)newArray, theSize);
DisposePtr((Ptr)registeredSounds);
registeredSounds = newArray; // replace old one with new one
currentBlockSizeForRegisteredSounds += kRegisterSoundBlockSize; // increase our tollerance
}
}
// we have enough room in our current array or the array has been enlarged,
// so put our reference in the next place
registeredSounds[totalRegisteredSounds-1] = theSound;
}
}
}
}
static void HYP_UnregisterThisSound(SndReference theSound)
{
register short int count;
// walk through our reference array and remove by setting the placeholder to NULL, our
// reference. We never shrink the reference array because it will never get that big, and
// its wastes time
if (registeredSounds)
{
for (count = 0; count < totalRegisteredSounds; count++)
{
if (registeredSounds[count] == theSound)
{
registeredSounds[count] = NULL; // ok remove from our list
}
}
}
}
static void HYP_UnregisterAllSounds(void)
{
register short int count;
// walk through our reference array and remove by setting the placeholder to NULL, our
// reference. At this point dispose of the reference array too
if (registeredSounds)
{
for (count = 0; count < totalRegisteredSounds; count++)
{
if (registeredSounds[count])
{
registeredSounds[count] = NULL; // ok remove from our list
}
}
DisposePtr((Ptr)registeredSounds);
registeredSounds = NULL;
}
}
// Public functions
//
void HY_SetSoundVBCallBack(void (*theProc)(void))
{
vblankProcPtr = theProc;
}
#define kCompressionPacketSize 6 // 2 bytes at 3:1 is 6 bytes for a packet
// 1 byte at 6:1 is 6 bytes also
Handle HY_GetMACESound(register CmpSoundHeaderPtr pSndBuffer,
Ptr *pWave, long *length,
long *pLoopStart, long *loopend,
long *rate)
{
Ptr inBuffer, outBuffer, theInState, theOutState;
CmpSoundHeader * csndHeaderPtr;
Handle outHandle;
unsigned long sampleCount;
long buffLen;
outHandle = NULL;
csndHeaderPtr = (CmpSoundHeaderPtr) pSndBuffer;
if (csndHeaderPtr->compressionID) // see if sound is compressed
{
sampleCount = csndHeaderPtr->numFrames; // get number of number of frames
buffLen = sampleCount * kCompressionPacketSize; // bufferLen = number of frames * packet size */
theInState = NewPtrClear(128L); // allocate working buffers
theOutState = NewPtrClear(128L);
outHandle = NewHandleClear(buffLen);
if ((theInState && theOutState && outHandle))
{
HLock(outHandle);
outBuffer = *outHandle;
if ((inBuffer = csndHeaderPtr->samplePtr) == NULL) // get ptr to sample data
{
inBuffer = (Ptr) csndHeaderPtr->sampleArea;
} /* decompress sound */
switch(csndHeaderPtr->compressionID) // MACE compression ID's only
{
case threeToOne:
Exp1to3(inBuffer, outBuffer, sampleCount, (StateBlockPtr)theInState, (StateBlockPtr)theOutState,
csndHeaderPtr->numChannels, 1);
break;
case sixToOne:
Exp1to6(inBuffer,outBuffer, sampleCount, (StateBlockPtr)theInState, (StateBlockPtr)theOutState,
csndHeaderPtr->numChannels, 1);
break;
default:
BlockMove(inBuffer, outBuffer, sampleCount);
break;
}
*pWave = outBuffer;
*length = buffLen;
*pLoopStart = csndHeaderPtr->loopStart;
*loopend = csndHeaderPtr->loopEnd;
*rate = csndHeaderPtr->sampleRate;
}
if (theInState)
{
DisposePtr(theInState);
}
if (theOutState)
{
DisposePtr(theOutState);
}
}
return outHandle;
}
void HY_RegisterThisSound(SndReference theSound)
{
if (theSound)
{
HLock(theSound);
HYP_RegisterThisSound(theSound); // register this sound
}
}
SndReference HY_GetSoundResource(short int resourceID)
{
Handle theSoundData;
SndReference theSndRef;
theSndRef = NULL;
theSoundData = GetResource('snd ', resourceID); // get the resource data type 'snd '
if (theSoundData)
{
HLock(theSoundData); // lock it down
HYP_RegisterThisSound(theSoundData); // register this sound
theSndRef = (SndReference)theSoundData;
}
return theSndRef;
}
void HY_UnregisterSoundResource(SndReference theSound)
{
if (HYP_IsThisSoundRegistered(theSound)) // only work with sounds that have been access through our API
{
HYP_UnregisterThisSound(theSound);
HUnlock((Handle)theSound);
}
}
void HY_UnregisterAllSoundResources(void)
{
register short int count;
// walk through our reference array and remove by setting the placeholder to NULL, our
// reference. At this point dispose of the reference array too
if (registeredSounds)
{
for (count = 0; count < totalRegisteredSounds; count++)
{
if (registeredSounds[count])
{
HY_UnregisterSoundResource(registeredSounds[count]); // ok remove from our list
}
}
HYP_UnregisterAllSounds();
}
}
Boolean HY_Is16BitAvailable(void)
{
long feature;
feature = 0;
if (Gestalt(gestaltSoundAttr, &feature) == noErr)
{
if (feature & (1<<gestalt16BitSoundIO))
{
return TRUE;
}
}
return FALSE;
}
Boolean HY_IsStereoAvailable(void)
{
long feature;
feature = 0;
if (Gestalt(gestaltSoundAttr, &feature) == noErr)
{
if (feature & (1<<gestaltStereoCapability))
{
return TRUE;
}
}
return FALSE;
}
OSErr HY_Setup(short int featureMask, short int maxVoices)
{
OSErr theErr;
short int count;
register MM_SoundVoice *pVoice;
register long features;
SoundVolume fullVolume;
if (HYP_IsNewSoundManagerInstalled())
{
theErr = noErr;
maxSoundVoices = 0;
fullVolume.left = kFullVolume;
fullVolume.right = kFullVolume;
registeredSounds = NULL; // no sounds registered
#if GENERATING68K == 1
globalA5 = (long)SetCurrentA5(); // we do this here, so HY_PlaySample can be called via interrupt
#else
globalA5 = 0;
#endif
theSoundVoices = (MM_SoundVoice *)NewPtrClear((long)sizeof(MM_SoundVoice) * maxVoices);
if (theSoundVoices)
{
#ifdef __MIXEDMODE__
theMasterSoundCallbackProcPtr = NewSndCallBackProc(HYP_HandleSoundDoneCallBack);
#else
theMasterSoundCallbackProcPtr = (void *)HYP_HandleSoundDoneCallBack;
#endif
features = 0L;
if ((featureMask & kUseStereo) == kUseStereo)
{
features |= initStereo; // stereo sound support
}
else
{
features |= initMono; // mono sound support
}
if ((featureMask & kMaxQuality) == kMaxQuality)
{
features |= initNoDrop; // keep interpolation on, turn off drop sample convertion (CPU heavy)
}
else
{
features |= initNoInterp; // turn off interpolation
}
for (count = 0; count < maxVoices; count++)
{
pVoice = &theSoundVoices[count];
theErr = SndNewChannel(&pVoice->theChannel,
sampledSynth,
features,
theMasterSoundCallbackProcPtr);
if (theErr)
{ /* we failed, so bail
*/
pVoice->theChannel = NULL;
HY_Cleanup();
break;
}
else
{ // success
maxSoundVoices++; // we increment our global in case we fail so we can back
// out easily
pVoice->voiceActive = FALSE;
pVoice->voicePaused = FALSE;
pVoice->filePlay = FALSE;
pVoice->theChannel->callBack = theMasterSoundCallbackProcPtr; // extra, just in case
pVoice->theChannel->userInfo = globalA5;
}
}
for (count = 0; count < maxVoices; count++)
{
HY_SetVolume(count, fullVolume);
if (HY_Is16BitAvailable())
{
HY_SetRate(count, rate22050hz);
}
else
{
HY_SetRate(count, rate22khz);
}
}
HYP_SetupQueues();
HYP_SetupVBLTask();
}
else
{
theErr = memFullErr;
}
}
else
{
theErr = smRevisionErr; // we can only work with Sound Manager 3.0. We're using cool features of SM 3.0
}
return theErr;
}
OSErr HY_Cleanup(void)
{
register short int count;
HYP_CleanupVBLTask(); // clean up our VBL task
HY_UnregisterAllSoundResources(); // clean up and remove all references to sounds. This calls HYP_UnregisterAllSounds
if (theSoundVoices)
{
for (count = 0; count < maxSoundVoices; count++)
{
if (theSoundVoices[count].theChannel)
{
HY_StopSample(count);
SndDisposeChannel(theSoundVoices[count].theChannel, TRUE);
theSoundVoices[count].theChannel = NULL;
}
}
#ifdef __MIXEDMODE__
if (theMasterSoundCallbackProcPtr)
{
DisposeRoutineDescriptor(theMasterSoundCallbackProcPtr);
}
#endif
theMasterSoundCallbackProcPtr = NULL;
DisposePtr((Ptr)theSoundVoices);
theSoundVoices = NULL;
}
return noErr;
}
Boolean HY_Active(void)
{
return (theSoundVoices) ? TRUE : FALSE;
}
OSErr HY_PlaySample( short int voiceNumber, // voice to play sample on
void *pSample, // morph data pointer
long length, // sample length
UnsignedFixed rate, // Fixed 16.16 value
short dataBitSize, // 8 or 16 bit data
short channelSize, // 1 or 2 channels of date
CustomCallbackProc customCallback, // callback when finished, event what
long userData,
Boolean killSound)
{
SndCommand theCmd;
register MM_SoundVoice *pVoice;
register OSErr theErr;
ExtSoundHeader theSndBuffer;
theErr = noErr;
if (voiceNumber == kUseAnyVoice)
{
voiceNumber = HYP_GetFreeVoice();
if (voiceNumber == kUseAnyVoice)
{
theErr = channelBusy;
}
}
if (length < 1) // data length greater than zero
{
theErr = buffersTooSmall;
}
if ( (dataBitSize != 8) && (dataBitSize != 16) ) // sample bit size is 8 or 16 bits
{
theErr = badFormat;
}
if ( (channelSize != 1) && (channelSize != 2) ) // mono or stereo
{
theErr = badFormat;
}
if (rate < 0x10000L) // sample rate is at least 1.0
{
theErr = siInvalidSampleRate;
}
if (theErr == noErr)
{
pVoice = HYP_GetPrivateDataFromVoice(voiceNumber); // voice to play on is in range
if (pVoice)
{
if (pVoice->voiceActive)
{
if (killSound)
{
HY_StopSample(voiceNumber);
}
else
{
theErr = channelBusy;
}
}
if (theErr == noErr)
{
pVoice->customCallback = customCallback;
pVoice->userData = userData;
/* Play sample */
theSndBuffer.samplePtr = (Ptr)pSample;
theSndBuffer.numChannels = channelSize;
theSndBuffer.sampleRate = rate;
theSndBuffer.loopStart = 0; // Apple Sound Manager looping doesn't work for one shot sounds
theSndBuffer.loopEnd = 0;
theSndBuffer.encode = extSH;
theSndBuffer.baseFrequency = 0;
theSndBuffer.numFrames = length;
// theSndBuffer.AIFFSampleRate = 0;
theSndBuffer.markerChunk = NULL;
theSndBuffer.instrumentChunks = NULL;
theSndBuffer.AESRecording = NULL;
theSndBuffer.sampleSize = dataBitSize;
theSndBuffer.futureUse1 = 0;
theSndBuffer.futureUse2 = 0;
theSndBuffer.futureUse3 = 0;
theSndBuffer.futureUse4 = 0;
pVoice->theSndBuffer = theSndBuffer;
theCmd.param1 = 0;
theCmd.param2 = (long)&theSndBuffer;
theCmd.cmd = bufferCmd;
theErr = SndDoCommand(pVoice->theChannel, &theCmd, FALSE); // start sound
if (theErr == noErr)
{
theErr = HYP_PostCallbackFunction(pVoice->theChannel, (long)pVoice);
}
pVoice->voiceActive = TRUE;
}
}
else
{
theErr = qtParamErr;
}
}
if (theErr)
{
DEBUGSTR("\pError in HY_PlaySample");
}
return theErr;
}
void HY_StopSample(register short int voiceNumber)
{
register MM_SoundVoice *pVoice;
SndCommand theCmd;
if (voiceNumber == kUseAnyVoice)
{
voiceNumber = HYP_GetFreeVoice();
}
pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
if (pVoice)
{
if (HY_IsVoiceEmpty(voiceNumber) == FALSE)
{
if (pVoice->filePlay)
{
SndStopFilePlay(pVoice->theChannel, TRUE);
FSClose(pVoice->fileRef);
pVoice->filePlay = FALSE;
pVoice->fileRef = 0;
}
theCmd.param1 = 0;
theCmd.param2 = 0;
theCmd.cmd = quietCmd;
SndDoImmediate(pVoice->theChannel, &theCmd);
theCmd.cmd = flushCmd;
SndDoImmediate(pVoice->theChannel, &theCmd);
if (pVoice->customCallback)
{
(*pVoice->customCallback)( voiceNumber,
kSoundDoneForcedStop,
pVoice->userData);
}
pVoice->voiceActive = FALSE;
}
}
}
OSErr HY_PlaySoundHandle( short int voiceNumber,
SndReference theSound,
CustomCallbackProc customCallback,
long userData,
Boolean killSound)
{
register MM_SoundVoice *pVoice;
register OSErr theErr;
theErr = noErr;
if (voiceNumber == kUseAnyVoice)
{
voiceNumber = HYP_GetFreeVoice();
if (voiceNumber == kUseAnyVoice)
{
theErr = channelBusy;
}
}
// only work with sounds that have been access through our API
if ( (theErr == noErr) && (HYP_IsThisSoundRegistered(theSound)))
{
pVoice = HYP_GetPrivateDataFromVoice(voiceNumber); // voice to play on is in range
if (pVoice)
{
if (pVoice->voiceActive)
{
if (killSound)
{
HY_StopSample(voiceNumber);
}
else
{
theErr = channelBusy;
}
}
if (theErr == noErr)
{
pVoice->customCallback = customCallback;
pVoice->userData = userData;
// Play sound sample. This allows the sound reference to be a MACE compressed sound, 8 or 16 bit, mono,
// stereo, or whatever.
theErr = SndPlay(pVoice->theChannel, (SndListHandle)theSound, TRUE);
if (theErr == noErr)
{
// now post a callback to process this sample when its finished
theErr = HYP_PostCallbackFunction(pVoice->theChannel, (long)pVoice);
pVoice->voiceActive = TRUE;
}
}
}
}
return noErr;
}
Boolean HY_IsVoiceEmpty(register short int voiceNumber)
{
SCStatus status;
register OSErr theErr;
register MM_SoundVoice *pVoice;
register Boolean empty;
empty = TRUE;
if (voiceNumber == kUseAnyVoice)
{
voiceNumber = HYP_GetFreeVoice();
}
pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
if (pVoice)
{
// Our voiceActive works because we set it at the end of a sample playback. But we're going to query
// the Sound Manager, just in case it fails to call our callback that sets the flag and we'll store the
// query in our variable voiceActive;
empty = ! pVoice->voiceActive;
theErr = SndChannelStatus(pVoice->theChannel, sizeof(SCStatus), &status);
if (theErr == noErr)
{
if (pVoice->voiceActive != status.scChannelBusy)
{
pVoice->voiceActive = status.scChannelBusy;
}
empty = ! pVoice->voiceActive;
}
}
return empty;
}
void HY_SetRate(short int voiceNumber, UnsignedFixed newRate)
{
register MM_SoundVoice *pVoice;
SndCommand theCmd;
if (voiceNumber == kUseAnyVoice)
{
voiceNumber = HYP_GetFreeVoice();
}
pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
if (pVoice)
{
pVoice->lastRate = newRate;
// Convert the passed rate into a relative sample multipler
newRate = UnsignedFixedMulDiv(newRate, 0x10000, rate22khz);
theCmd.param1 = 0;
theCmd.param2 = (long)newRate;
theCmd.cmd = rateCmd;
SndDoImmediate(pVoice->theChannel, &theCmd); // change rate now for this channel
}
}
UnsignedFixed HY_GetRate(short int voiceNumber)
{
register MM_SoundVoice *pVoice;
UnsignedFixed oldRate;
SndCommand theCmd;
if (voiceNumber == kUseAnyVoice)
{
voiceNumber = HYP_GetFreeVoice();
}
oldRate = rate22khz;
pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
if (pVoice)
{
oldRate = pVoice->lastRate;
if (pVoice->voiceActive)
{
theCmd.param1 = 0;
theCmd.param2 = (long)&oldRate;
theCmd.cmd = getRateCmd;
SndDoImmediate(pVoice->theChannel, &theCmd); // get the current rate for this channel
// Use this cool Toolbox routine to determine the actual sample rate
oldRate = UnsignedFixedMulDiv(rate22khz, oldRate, 0x10000);
}
}
return oldRate;
}
void HY_SetVolume(short int voiceNumber, SoundVolume newVolume)
{
register MM_SoundVoice *pVoice;
SndCommand theCmd;
if (voiceNumber == kUseAnyVoice)
{
voiceNumber = HYP_GetFreeVoice();
}
pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
if (pVoice)
{
theCmd.param1 = 0;
// by breaking down the volumes here, we become a little more flexible to compilers
theCmd.param2 = (long)((long)newVolume.right << 16L | (newVolume.left & 0xFFFF));
theCmd.cmd = volumeCmd;
SndDoImmediate(pVoice->theChannel, &theCmd); // change volume now for this channel
}
}
SoundVolume HY_GetVolume(short int voiceNumber)
{
register MM_SoundVoice *pVoice;
SndCommand theCmd;
SoundVolume oldVolume;
long soundVolume;
if (voiceNumber == kUseAnyVoice)
{
voiceNumber = HYP_GetFreeVoice();
}
*((long *)&oldVolume) = -1L;
pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
if (pVoice)
{
soundVolume = 0;
theCmd.param1 = 0;
theCmd.param2 = (long)&soundVolume;
theCmd.cmd = getVolumeCmd;
SndDoImmediate(pVoice->theChannel, &theCmd); // get the current volume for this channel
// by breaking down the volumes here, we become a little more flexible to compilers
oldVolume.right = soundVolume >> 16L;
oldVolume.left = soundVolume & 0xFFFFL;
}
return oldVolume;
}
void HY_SetStereoPosition(short int voiceNumber, short positionMask)
{
register MM_SoundVoice *pVoice;
SoundVolume theVolume;
if (voiceNumber == kUseAnyVoice)
{
voiceNumber = HYP_GetFreeVoice();
}
pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
if (pVoice)
{
theVolume.right = kFullVolume + positionMask;
theVolume.left = kFullVolume - positionMask;
HY_SetVolume(voiceNumber, theVolume);
}
}
short HY_GetStereoPosition(short int voiceNumber)
{
register MM_SoundVoice *pVoice;
SoundVolume theVolume;
short positionMask;
if (voiceNumber == kUseAnyVoice)
{
voiceNumber = HYP_GetFreeVoice();
}
pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
if (pVoice)
{
theVolume = HY_GetVolume(voiceNumber);
positionMask = theVolume.right - theVolume.left;
}
return positionMask;
}
void HY_ServiceTasks(void)
{
register short int count;
SCStatus status;
register OSErr theErr;
register MM_SoundVoice *pVoice;
HYP_ProcessNextCallbackQueue();
for (count = 0; count < maxSoundVoices; count++)
{
pVoice = &theSoundVoices[count];
if (pVoice->theChannel)
{
theErr = SndChannelStatus(pVoice->theChannel, sizeof(SCStatus), &status);
if (theErr == noErr)
{
if (pVoice->voiceActive != status.scChannelBusy)
{
pVoice->voiceActive = status.scChannelBusy;
}
}
}
}
}
OSErr HY_PauseHardware(void)
{
register MM_SoundVoice *pVoice;
register short int count;
HYP_CleanupVBLTask();
for (count = 0; count < maxSoundVoices; count++)
{
pVoice = &theSoundVoices[count];
if (pVoice->theChannel)
{
if (pVoice->voicePaused == FALSE)
{
pVoice->voicePaused = TRUE;
if (pVoice->voiceActive)
{
if (pVoice->filePlay)
{
SndPauseFilePlay(pVoice->theChannel); // pause file playback
}
}
}
}
}
return noErr;
}
OSErr HY_ResumeHardware(void)
{
register MM_SoundVoice *pVoice;
register short int count;
HYP_SetupVBLTask();
for (count = 0; count < maxSoundVoices; count++)
{
pVoice = &theSoundVoices[count];
if (pVoice->theChannel)
{
if (pVoice->voicePaused)
{
pVoice->voicePaused = FALSE;
if (pVoice->voiceActive)
{
if (pVoice->filePlay)
{
SndPauseFilePlay(pVoice->theChannel); // resume file playback
}
}
}
}
}
return noErr;
}
OSErr HY_StartFilePlay(short int voiceNumber,
FSSpec *pFile,
CustomCallbackProc customCallback,
long userData,
long bufferSize,
Boolean killSound)
{
register MM_SoundVoice *pVoice;
register OSErr theErr;
theErr = noErr;
if (voiceNumber == kUseAnyVoice)
{
voiceNumber = HYP_GetFreeVoice();
if (voiceNumber == kUseAnyVoice)
{
theErr = channelBusy;
}
}
if (theErr == noErr)
{
pVoice = HYP_GetPrivateDataFromVoice(voiceNumber); // voice to play on is in range
if (pVoice)
{
if (pVoice->voiceActive)
{
if (killSound)
{
HY_StopSample(voiceNumber);
}
else
{
theErr = channelBusy;
}
}
if (theErr == noErr)
{
pVoice->customCallback = customCallback;
pVoice->userData = userData;
pVoice->theChannel->userInfo = globalA5;
pVoice->fileSpec = *pFile;
// We could use the cooler file open function, but it only works with System 7 or better. Sound Manager 3.0
// can be installed on a System eariler than 7.0
// theErr = FSpOpenDF(pFile, fsRdPerm, &pVoice->fileRef);
theErr = HOpen(pFile->vRefNum, pFile->parID, pFile->name, fsRdPerm, &pVoice->fileRef);
if (theErr == noErr)
{
theErr = SndStartFilePlay(pVoice->theChannel,
pVoice->fileRef, 0,
bufferSize, NULL,
NULL,
NewFilePlayCompletionProc(HYP_FilePlayCompletionDone),
TRUE);
if (theErr == noErr)
{
pVoice->filePlay = TRUE;
pVoice->voiceActive = TRUE;
}
}
}
}
}
return theErr;
}
OSErr HY_GetSoundResourceInformation(Handle theSnd, long *pLoopStart, long *pLoopEnd,
long *pSampleOffsetStart, long *pTotalSize, short *pBaseKey,
short int *pNumChannels, short int *pBitSize,
UnsignedFixed *pRate,
short int *pCompressionType)
{
register SoundHeader * pSndBuffer;
register CmpSoundHeader * pCmpBuffer;
register ExtSoundHeader * pExtBuffer;
short int soundFormat;
short int numSynths, numCmds;
long offset;
register Ptr pSndFormat;
OSErr theErr;
theErr = badFormat;
*pSampleOffsetStart = 0;
*pTotalSize = 0;
*pLoopStart = 0;
*pLoopEnd = 0;
*pBaseKey = 0;
*pCompressionType = notCompressed;
*pNumChannels = 1; // defaults for standard header
*pBitSize = 8;
*pRate = 0;
if (theSnd)
{
HLock(theSnd);
pSndFormat = (Ptr)*theSnd;
soundFormat = *(short int *)pSndFormat;
switch (soundFormat)
{
case 1: // format 1 sound
// look inside the format 1 resource and decode offsets
numSynths = ((short int *)pSndFormat)[1]; // get number of synths
numCmds = *(short int *)(pSndFormat + 4 + numSynths * 6); // get number of commands
break;
case 2: // format 2 sound
numSynths = 0; // format 2 has none
numCmds = ((short int *)pSndFormat)[2];
break;
default:
soundFormat = -1;
break;
}
if (soundFormat != -1) /* did we get the right format? */
{
/* compute address of sound header.
*/
offset = 6 + 6 * numSynths + 8 * numCmds;
pSndBuffer = (SoundHeader *) (StripAddress(*theSnd) + offset);
switch (pSndBuffer->encode)
{
case stdSH: // standard header
*pSampleOffsetStart = (long)&pSndBuffer->sampleArea[0] - (long)((Byte *)*theSnd);
*pTotalSize = pSndBuffer->length;
*pLoopStart = pSndBuffer->loopStart;
*pLoopEnd = pSndBuffer->loopEnd;
*pBaseKey = pSndBuffer->baseFrequency;
*pRate = pSndBuffer->sampleRate;
theErr = noErr;
break;
case extSH: // extened header
pExtBuffer = (ExtSoundHeader *)pSndBuffer;
*pSampleOffsetStart = (long)&pExtBuffer->sampleArea[0] - (long)((Byte *)*theSnd);
*pNumChannels = pExtBuffer->numChannels;
*pBitSize = pExtBuffer->sampleSize;
*pTotalSize = pExtBuffer->numFrames * (*pNumChannels) * (*pBitSize / 8);
*pLoopStart = pExtBuffer->loopStart;
*pLoopEnd = pExtBuffer->loopEnd;
*pBaseKey = pExtBuffer->baseFrequency;
*pRate = pExtBuffer->sampleRate;
theErr = noErr;
break;
case cmpSH: // compressed header
pCmpBuffer = (CmpSoundHeader *)pSndBuffer;
*pSampleOffsetStart = (long)&pCmpBuffer->sampleArea[0] - (long)((Byte *)*theSnd);
*pNumChannels = pCmpBuffer->numChannels;
*pBitSize = pCmpBuffer->sampleSize;
*pTotalSize = pCmpBuffer->numFrames * (*pNumChannels) * (*pBitSize / 8);
*pLoopStart = pCmpBuffer->loopStart;
*pLoopEnd = pCmpBuffer->loopEnd;
*pBaseKey = pCmpBuffer->baseFrequency;
*pRate = pCmpBuffer->sampleRate;
*pCompressionType = pCmpBuffer->compressionID;
theErr = noErr;
break;
}
}
HUnlock(theSnd);
}
return theErr;
}
OSErr HY_CreateAIFFFileFromPtr(FSSpec *pFile, Ptr pSample, long dataLength, UnsignedFixed sampleRate,
short bitSize, short numChannels)
{
OSErr theErr;
short int fileRef;
long length;
theErr = badFormat;
if ( (bitSize == 8) || (bitSize == 16) || (numChannels == 1) || (numChannels == 2) )
{
theErr = FSpCreate(pFile, 'hlly', AIFFID , 0);
if (theErr == noErr)
{
theErr = FSpOpenDF(pFile, fsRdWrPerm, &fileRef);
if (theErr == noErr)
{
SetFPos(fileRef, fsFromStart, 0L);
theErr = SetupAIFFHeader(fileRef, numChannels, sampleRate, bitSize, NoneType,
0L, dataLength);
if (theErr == noErr)
{
length = dataLength * numChannels * (bitSize / 8);
theErr = FSWrite(fileRef, &length, pSample);
if (theErr == noErr)
{
SetFPos(fileRef, fsFromStart, 0L);
theErr = SetupAIFFHeader(fileRef, numChannels, sampleRate, bitSize, NoneType,
length, dataLength);
}
FSClose(fileRef);
}
}
}
}
return theErr;
}
Handle HY_CreateSndResourceFromPtr(Ptr pSample, long dataLength, UnsignedFixed sampleRate,
short bitSize, short numChannels,
short int baseFreq)
{
OSErr theErr;
Handle theSoundHeader;
Handle theSound;
short headerLength;
theSound = NULL;
// first allocate enough of a handle to setup the header information
theSoundHeader = NewHandleClear(200L);
if (theSoundHeader)
{
theErr = SetupSndHeader((SndListHandle)theSoundHeader, numChannels, sampleRate, bitSize, 'NONE',
baseFreq, 0, &headerLength);
if (theErr == noErr)
{
// ok, now we know how large header info is, so we can block move the sample data right after
theSound = NewHandle(headerLength + dataLength);
if (theSound)
{
HLock(theSound);
BlockMove(*theSoundHeader, *theSound, headerLength);
BlockMove(pSample, ((Byte *)*theSound) + headerLength, dataLength);
HUnlock(theSound);
DisposeHandle(theSoundHeader);
// ok, now set the new length of the data
theErr = SetupSndHeader((SndListHandle)theSound, numChannels, sampleRate, bitSize, 'NONE',
baseFreq, dataLength, &headerLength);
if (theErr)
{
DisposeHandle(theSound);
theSound = NULL;
}
}
}
else
{
DisposeHandle(theSoundHeader);
}
}
return theSound;
}
Handle HY_CreateMACESndResourceFromPtr(short maceType, Ptr pSample, long dataLength,
UnsignedFixed sampleRate,
short bitSize, short numChannels,
short int baseFreq)
{
OSErr theErr;
Handle theSoundHeader;
Handle theSound;
short headerLength;
long compressLength;
OSType compressionType;
Byte compressInState[128];
Byte compressOutState[128];
Byte * pData;
long count;
theErr = badFormat;
theSound = NULL;
if ( (bitSize == 8) && (numChannels == 1) ) // MACE only supports 8 bit samples. Hollywood only supports mono
{
switch (maceType)
{
case threeToOne:
compressionType = 'MAC3';
compressLength = dataLength / 3L;
theErr = noErr;
break;
case sixToOne:
compressionType = 'MAC6';
compressLength = dataLength / 6L;
theErr = noErr;
break;
}
}
if (theErr == noErr)
{
// first allocate enough of a handle to setup the header information
theSoundHeader = NewHandleClear(200L);
if (theSoundHeader)
{
theErr = SetupSndHeader((SndListHandle)theSoundHeader, 1, sampleRate, bitSize, compressionType,
baseFreq, 0, &headerLength);
if (theErr == noErr)
{
// ok, now we know how large header info is, so we can block move the sample data right after
theSound = NewHandle(headerLength + dataLength);
if (theSound)
{
HLock(theSound);
BlockMove(*theSoundHeader, *theSound, headerLength);
// Clear state data for MACE compressor
for (count = 0; count < 128; count++)
{
compressInState[count] = 0;
compressOutState[count] = 0;
}
// MACE supports stereo compressed data, but we don't bother to put it together here.
// In order to create stereo compressed data, you need to build to buffers, one for each
// channel. Compress each channel into that buffer by calling CompXto1 with the last parameter
// set to the channel you are compressing. Then to rebuild the final data, you need to interleave
// the two buffers into one buffer for the final output. Simple huh! :o
pData = ((Byte *)*theSound) + headerLength;
switch (maceType)
{
case threeToOne:
Comp3to1(pSample, pData, dataLength,
NULL, NULL, 1, 1);
// compressInState, compressOutState, 1, 1);
break;
case sixToOne:
Comp6to1(pSample, pData, dataLength,
NULL, NULL, 1, 1);
// compressInState, compressOutState, 1, 1);
break;
}
HUnlock(theSound);
SetHandleSize(theSound, headerLength + compressLength + 6);
theErr = MemError();
DisposeHandle(theSoundHeader);
if (theErr == noErr)
{
// ok, now set the new length of the data
theErr = SetupSndHeader((SndListHandle)theSound, 1, sampleRate, bitSize, compressionType,
baseFreq, compressLength, &headerLength);
}
if (theErr)
{
DisposeHandle(theSound);
theSound = NULL;
}
}
}
else
{
if (theSound)
{
DisposeHandle(theSoundHeader);
}
}
}
}
return theSound;
}
// EOF of Hollywood.c